<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2018\9\27 0027
 * Time: 9:42
 */

namespace app\Lib;


use app\Lib\interfaceApi\ApiDemo;
use Exception;
use think\Config;
use think\Debug;
use think\Log;
use think\Request;

class TopClient
{

    protected $appkey = "11211";

    protected $secretKey = "56565656";
    public $format = "json";

    public $connectTimeout;

    public $readTimeout;

    public $method;

    public $sign;
    public $requestSignTime;
    public $requestTime;
    public $version;
    protected $debug=0;

    /** 是否打开入参check**/
    public $checkRequest = true;

    protected $signMethod = "md5";
    protected $responseHttpStatusCode = "";
    /** @var ApiDemo $apiDemo  */
    private  $apiDemo;


    public function __construct(ApiDemo $apiDemo)
    {
        $this->apiDemo = $apiDemo;
        $this->debug = 1;
        $this->readTimeout = 5;
    }

    protected function generateSign($params)
    {
        ksort($params);

        $stringToBeSigned = $this->secretKey;
        foreach ($params as $k => $v) {
            if (!is_array($v)) {
                if ("@" != substr($v, 0, 1)) {
                    $stringToBeSigned .= "$k$v";
                }
            } else {
                if ("@" != substr(json_encode($v), 0, 1)) {
                    $stringToBeSigned .= $k . json_encode($v);
                }
            }

        }
        unset($k, $v);
        $stringToBeSigned .= $this->secretKey;
        //echo $stringToBeSigned;

        return strtoupper(md5($stringToBeSigned));
    }

    /**
     * @param $url
     * @param null $postFields
     * @return mixed
     * @throws Exception
     */
    public function curl($url, $postFields = null)
    {
        //exit();
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $API_PROXY=trim(Config::get("API_PROXY"));
        if($API_PROXY){
            $API_PROXY_CONFIG=explode(":",$API_PROXY);
            curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //代理认证模式
            curl_setopt($ch, CURLOPT_PROXY, $API_PROXY_CONFIG[0]); //代理服务器地址
            curl_setopt($ch, CURLOPT_PROXYPORT,$API_PROXY_CONFIG[1]); //代理服务器端口
        }

//        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts

        if ($this->readTimeout) {
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->readTimeout);
        }
        if ($this->connectTimeout) {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
        }
        curl_setopt($ch, CURLOPT_USERAGENT, "top-sdk-php");
        //https 请求
        if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }


        if (is_array($postFields) && 0 < count($postFields)) {
            $postBodyString = "";
            $postMultipart = false;
            foreach ($postFields as $k => $v) {
                if (is_array($v)) {

                    $postBodyString .= "$k=" . urlencode(json_encode($v)) . "&";

                } else {
                    if ("@" != substr($v, 0, 1))//判断是不是文件上传
                    {
                        $postBodyString .= "$k=" . urlencode($v) . "&";
                    } else//文件上传用multipart/form-data，否则用www-form-urlencoded
                    {
                        $postMultipart = true;
                        if (class_exists('\CURLFile')) {
                            $postFields[$k] = new \CURLFile(substr($v, 1));
                        }
                    }
                }

            }
            unset($k, $v);
            curl_setopt($ch, CURLOPT_POST, true);
            if ($postMultipart) {
                if (class_exists('\CURLFile')) {
                    curl_setopt($ch, CURLOPT_SAFE_UPLOAD, true);
                } else {
                    if (defined('CURLOPT_SAFE_UPLOAD')) {
                        curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
                    }
                }

                curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields);
            } else {
                $header = array("content-type: application/x-www-form-urlencoded; charset=UTF-8");
                curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
                curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
            }
        }
        $reponse = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch), 90001);
        } else {

            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $this->responseHttpStatusCode=$httpStatusCode;
        }
        curl_close($ch);
        return $reponse;
    }

    /**
     * @param $url
     * @param null $postFields
     * @param null $fileFields
     * @return mixed
     * @throws Exception
     */
    public function curl_with_memory_file($url, $postFields = null, $fileFields = null)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FAILONERROR, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $API_PROXY=trim(Config::get("API_PROXY"));
        if($API_PROXY){
            $API_PROXY_CONFIG=explode(":",$API_PROXY);
            curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); //代理认证模式
            curl_setopt($ch, CURLOPT_PROXY, $API_PROXY_CONFIG[0]); //代理服务器地址
            curl_setopt($ch, CURLOPT_PROXYPORT,$API_PROXY_CONFIG[1]); //代理服务器端口
        }
        if ($this->readTimeout) {
            curl_setopt($ch, CURLOPT_TIMEOUT, $this->readTimeout);
        }
        if ($this->connectTimeout) {
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->connectTimeout);
        }
        curl_setopt($ch, CURLOPT_USERAGENT, "top-sdk-php");
        //https 请求
        if (strlen($url) > 5 && strtolower(substr($url, 0, 5)) == "https") {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }
        //生成分隔符
        $delimiter = '-------------' . uniqid();
        //先将post的普通数据生成主体字符串
        $data = '';
        if ($postFields != null) {
            foreach ($postFields as $name => $content) {
                $data .= "--" . $delimiter . "\r\n";
                $data .= 'Content-Disposition: form-data; name="' . $name . '"';
                //multipart/form-data 不需要urlencode，参见 http:stackoverflow.com/questions/6603928/should-i-url-encode-post-data
                $data .= "\r\n\r\n" . $content . "\r\n";
            }
            unset($name, $content);
        }

        //将上传的文件生成主体字符串
        if ($fileFields != null) {
            foreach ($fileFields as $name => $file) {
                $data .= "--" . $delimiter . "\r\n";
                $data .= 'Content-Disposition: form-data; name="' . $name . '"; filename="' . $file['name'] . "\" \r\n";
                $data .= 'Content-Type: ' . $file['type'] . "\r\n\r\n";//多了个文档类型

                $data .= $file['content'] . "\r\n";
            }
            unset($name, $file);
        }
        //主体结束的分隔符
        $data .= "--" . $delimiter . "--";

        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
                'Content-Type: multipart/form-data; boundary=' . $delimiter,
                'Content-Length: ' . strlen($data))
        );
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data);

        $reponse = curl_exec($ch);
        unset($data);

        if (curl_errno($ch)) {
            throw new \Exception(curl_error($ch), 90001);
        } else {
            $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
            if (200 !== $httpStatusCode) {
                throw new \Exception($reponse, $httpStatusCode);
            }
        }
        curl_close($ch);
        return $reponse;
    }

    /**
     * @param null $session
     * @param null $bestUrl
     * @return mixed|\SimpleXMLElement|string
     * @throws Exception
     */
    public function execute($session = null, $bestUrl = null)
    {
        $this->responseHttpStatusCode="";
        $request = Request::instance();
        $ip = $request->ip();
        unset($request);
        /** @var ApiDemo  $request */
        $request = $this->apiDemo;
        //$result = new ResultSet();
        if ($this->checkRequest) {
            try {
                $request->check();
            } catch (\Exception $e) {
                throw $e;
            }
        }
        //组装系统参数
        $sysParams["appkey"] = $this->appkey;
        $sysParams["version"] = $request->apiVersion ? $request->apiVersion : "1.0";
        $sysParams["format"] = $this->format;
        $sysParams["signMethod"] = $this->signMethod;
        $sysParams["method"] = $request->getApiName();
        $sysParams["timestamp"] = date("Y-m-d H:i:s");
        if (null != $session) {
            $sysParams["session"] = $session;
        }
        //获取业务参数
        $apiParams = $request->getApiParas();

        //系统参数放入GET请求串
        $requestUrl = \app\Lib\Config::get("ApiUrl");
        if (!$requestUrl) throw new \Exception("API请求地址不存在");
        $requestUrl = $requestUrl . "?";
        //签名
        $sysParams["sign"] = $this->generateSign(array_merge($apiParams, $sysParams));
        $sysParams["ip"] =$ip;
        foreach ($sysParams as $sysParamKey => $sysParamValue) {
            $requestUrl .= "$sysParamKey=" . urlencode($sysParamValue) . "&";
        }

        $fileFields = array();
        foreach ($apiParams as $key => $value) {
            if (is_array($value) && array_key_exists('type', $value) && array_key_exists('content', $value)) {
                $value['name'] = $key;
                $fileFields[$key] = $value;
                unset($apiParams[$key]);
            }
        }
        $requestUrl = substr($requestUrl, 0, -1);
        //发起HTTP请求
        if( Config::get("APP_DEBUG")  ){
            Debug::remark('api_request_begin');
        }
        $Excep="";
        $resp="";
        try {
            if (count($fileFields) > 0) {
                $resp = $this->curl_with_memory_file($requestUrl, $apiParams, $fileFields);
            } else {
                $resp = $this->curl($requestUrl, $apiParams);
            }
            if($this->responseHttpStatusCode!=200){
                throw new Exception("api[".$request->getApiName()."]请求失败");
            }
        } catch (Exception $e) {
            $Excep=$e;
            if($e->getCode()==90001){
                $resp="接口请求超时，详情【".$e->getMessage()."】";
            }
        }
        if( Config::get("APP_DEBUG")  ){
            Debug::remark('api_request_end');
            $log=[
                "ApiName"=>$request->getApiName(),
                "requestUrl"=>$requestUrl,
                "post"=>$apiParams,
                "responseHttpStatusCode"=>$this->responseHttpStatusCode,
                "apiRequestTime"=>Debug::getRangeTime('api_request_begin','api_request_end',4).'s',
                "return"=>$resp,
                "string"=>"ApiName:【".$request->getApiName().'】,requestUrl:【  ' . $requestUrl .
                    " 】,POST内容:【" .var_export($apiParams,true).'】'
            ];
            Log::record($log, 'debug');
        }
        if($Excep){
            throw $Excep;
        }

        unset($apiParams);
        unset($fileFields);
        //解析TOP返回结果
        $respWellFormed = false;
        if ("json" == $this->format) {
            $respObject = json_decode($resp, true);
            if($respObject !== null) {
                $respWellFormed = true;
            }
            // dump($resp);
        } else if ("xml" == $this->format) {
            $respObject = @simplexml_load_string($resp);
            if($respObject !== false) {
                $respWellFormed = true;
            }
        }else{
            $respObject = $resp;
        }
        $respWellFormed;
        return $respObject;
    }

    public function debug($debug = 1)
    {
        $this->debug = $debug;
    }

    /**
     * @return array|string
     * @throws Exception
     * @throws \Exception
     */
    public function getSuccessData()
    {
        $returnData = [];
        $data = $this->execute();
        if ($data["code"] != 1) {
            $message = isset($data["result"]["message"]) ? $data["result"]["message"] : "";
            throw  new Exception($message?$message:"接口调用返回失败");
        }
        if ($data["code"] == 1) {
            if($data['result']['status']==1){
                $data = isset($data["result"]["data"]) ? $data["result"]["data"] : "";
            }else{
                $data = isset($data["result"]) ? $data["result"] : "";
            }

            $returnData = $data;
        }
        return $returnData;
    }


}